跳到主要内容

Rime 用户词典

数据结构

  • 采用 Google 开发的开源数据库 LevelDB 存储,是一个有序的键值数据库,可以想像成一个 C++ 的 TreeMap
  • 键 Key:是编码和汉字,以 \t 分隔。例如 "ming yue pin yin\t明月拼音",或者 "ggtt\t五笔"
  • 值 Value:是形如 c=? d=? t=? 的字符串,在 librime 中解析为 UserDbValue 类型
struct UserDbValue {
int commits = 0;
double dee = 0.0;
uint64_t tick = 0;
}
  • commits:上屏总次数
  • dee:当前的新鲜度(热乎度)
  • tick:这个词上次被使用的时候的时间戳

常规使用

d 公式

dnew=d0+dold×exp[(toldtnew)/200]d_{\mathrm{new}} = d_0+d_{\mathrm{old}}\times\exp[{(t_{\mathrm{old}} - t_{\mathrm{new}})/200}]
  • 阐释:d 公式模拟了一个人对一个概念的指数遗忘规律。每过 200 个时间戳,用户对一个词的新鲜度折旧为原来的 37%。不过,如果用户使用了这个词一次,新鲜度就会增加 d0d_0,这个值通常是 1。

p 公式

m=ct×(1exp(t/10000))10m=\frac ct\times(1-\exp(-t/10000))^{10} p={m+(0.5m)×d/kMd20m+(1m)×(4d/kM1)/3d>20p=\begin{cases}m+(0.5-m)\times d/k_M& d\le20\\m+(1-m)\times(4^{d/k_M}-1)/3& d>20\end{cases}

阐释:p 公式是根据 cc, dd 和当前时间 tt 来综合判断字词的排序的算法

  • mm 代表了这个词的频率的贡献。c/tc/t 是过去上屏的所有字词中这个词占的比例,后面的部分是这个比例的置信度。如果 t10000t\ll 10000,那么说明用户刚刚开始调教词库,这个时候经验频率的置信度不高,后面这个因子非常小,导致 mm 也非常小;反之,如果 t10000t\gg 10000,那么后面这个因子接近 1。
  • mm 后面的部分代表了这个词的新鲜度的贡献。特别的,如果这个词的新鲜度非常高,那么需要以指数级别来强调这个词(4d/kM4^{d/k_M} 部分)
  • 总之,如果用户刚开始使用,那么主要是新鲜度的贡献比较大;反之,是频率的贡献比较大。

条目创建

  • UserDictionary::UpdateEntry 函数负责新条目的创建,除了条目之外还接受参数 commitsnew_entry_prefix
  • TableTranslator::Memorize 函数分析上屏历史,调用 TableEncoder::DfsEncode 对已上屏字词枚举编码,然后再调用 UnityTableEncoder::CreateEntry 函数,最后调用 UserDictionary::UpdateEntry,此时 commits = 0new_entry_prefix"\177enc\037" 这个特殊的字符串用来标识这个字是自动造出来的词,价值不高。
  • UserDbValue 被初始化为 { commits = 0, dee = 0.1, tick = t }

条目更新

条目更新也是调用上面的这个函数,但是调用的时候 commits = 1v.commits += 1v.dee 按照上面的函数更新。特别的,如果这个条目更新是确认了之前新建的词,则特殊字符串会被删除,标志着它正式进入词库

条目删除(假删除)

ExpressEditor 可以通过 Shift+Delete 或者 Control+Delete 等快捷键删除用户词典的词条,但是并没有真删除,实际上是调用了 UpdateEntry(entry, -1)。此时 v.commits 会被加个负号作为标记(如果本来就是 0,则变成 -1),v.dee 也根据这个衰减。

声笔系列码新加了一个自动删除的功能,也会调用这个函数。

条目查询

查询的时候是用输入的编码在 LevelDB 中查找。因为 LevelDB 是有序的键值存储,所以在没有精确匹配的情况下,会找到按字典序比这个大的条目。这也就是为什么 LevelDB 里的键是 "编码 + \t + 字词",但是只用编码查询也能查询到的原因。一般情况下,LevelDB 中所有匹配这个前缀的编码都会返回,不过声笔系列码中做了特殊判断,遍历之后返回权重最高的作为 3 码的候选(以声笔简码为例)。

最后调用 p 公式,然后取对数再加上 1(不知道为什么这么做),作为候选项的权重

批量处理

使用工具程序 rime_dict_manager 来完成

  • 需要关闭正在使用的输入法,释放词典文件
  • 将工作目录设置为「Rime 用户文件夹」

备份、合并词典快照

  • 备份:--backup dict_name
  • 合并:--restore xxx.userdb.txt

合并时,词频和 dee 会更新为二者的较大值,tick 更新为全局的最新值

导入、导出文本码表

  • 导入:--import dict_name import.txt
  • 导出:--export dict_name export.txt

导入、导出的格式为以制表符分隔的三列,分别为文字、编码、使用频次,但是文本码表因为没有 tick 和 dee 字段,所以包含的信息没有快照全面